/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   gbx-emu - mainwindow.cpp                                              *
 *   gbx-emu homepage: http://code.google.com/p/gbx-emu/                   *
 *   Copyright (C) 2009 olejl77@gmail.com                                  *
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <QtGui>
#include <math.h>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "debuggerwidget.h"
#include "aboutdialog.h"
#include "settingsdialog.h"
#include "../core/gameboy.h"
#include "../core/common.h"
#include "../core/cpu.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    _dbg = NULL;
    _cpu = NULL;
    _dbg = NULL;
    _dbgWidget = NULL;

    ui->setupUi(this);

    qRegisterMetaType<QImage>("QImage");
    connect(&_gb, SIGNAL(updateScreen(const QImage &)), this, SLOT(updatePixmap(const QImage &)));
    connect(&_gb, SIGNAL(newFps(double)), this, SLOT(updateFps(double)));

    connect(ui->actionE_xit,     SIGNAL(triggered()), this, SLOT(close()));
    connect(ui->action_Open_Rom, SIGNAL(triggered()), this, SLOT(open()));
    connect(ui->action_Start,    SIGNAL(triggered()), this, SLOT(start()));
    connect(ui->action_Stop,     SIGNAL(triggered()), this, SLOT(stop()));
    connect(ui->action_About,    SIGNAL(triggered()), this, SLOT(showAbout()));
    connect(ui->action_Settings, SIGNAL(triggered()), this, SLOT(showSettings()));

    setWindowTitle(tr("gbx-emu"));

    _log = new Log();
    _gb.init(this, _log);

    m_settings = new QSettings("gbx-emu", "gbx-emu");
    readSettings();
}

MainWindow::~MainWindow()
{
    delete ui;
    _dbg = NULL;
    _cpu = NULL;
    _dbg = NULL;
    _dbgWidget = NULL;
}

void MainWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    QPainter painter(this);
    painter.fillRect(rect(), Qt::black);

    if (pixmap.isNull()) {
        painter.setPen(Qt::white);
        painter.drawText(rect(), Qt::AlignCenter, tr("Emulation stopped..."));
    } else {
        painter.drawPixmap(pixmapOffset, pixmap);
    }
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    u8 key = 0;
    if (event->key() == m_down) {
        key = 0x80;
    } else if (event->key() == m_up) {
        key = 0x40;
    } else if (event->key() == m_left) {
        key = 0x20;
    } else if (event->key() == m_right) {
        key = 0x10;
    } else if (event->key() == m_start) {
        key = 0x08;
    } else if (event->key() == m_select) {
        key = 0x04;
    } else if (event->key() == m_b) {
        key = 0x02;
    } else if (event->key() == m_a) {
        key = 0x01;
    } else {
        QWidget::keyPressEvent(event);
        return;
    }
    if (_gb.isRunning() && (!event->isAutoRepeat())) {
        _cpu->keyPressed(key);
    }
}

void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    u8 key = 0;
    if (event->key() == m_down) {
        key = 0x80;
    } else if (event->key() == m_up) {
        key = 0x40;
    } else if (event->key() == m_left) {
        key = 0x20;
    } else if (event->key() == m_right) {
        key = 0x10;
    } else if (event->key() == m_start) {
        key = 0x08;
    } else if (event->key() == m_select) {
        key = 0x04;
    } else if (event->key() == m_b) {
        key = 0x02;
    } else if (event->key() == m_a) {
        key = 0x01;
    } else {
        QWidget::keyPressEvent(event);
        return;
    }

    if (_gb.isRunning() && (!event->isAutoRepeat())) {
        _cpu->keyReleased(key);
    }
}

void MainWindow::updatePixmap(const QImage &image)
{
    pixmap = QPixmap::fromImage(image);
    pixmapOffset = QPoint(0,25);
    update();
}

void MainWindow::updateFps(double fps)
{
    statusBar()->showMessage(QString("FPS: %1").arg(fps, 0, 'f', 1));
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event);
    stop();
    writeSettings();
}

void MainWindow::open()
{
    QString dir = m_settings->value("EmulatorSettings/RomDirectory", "./").toString();
    fileName = QFileDialog::getOpenFileName(this, tr("Choose Rom"), dir, tr("Gameboy Roms (*.gb)"));
    s32 last = fileName.lastIndexOf("/");
    dir = fileName;
    dir.chop(fileName.length() - last);
    if (!fileName.isEmpty()) {
        m_settings->setValue("EmulatorSettings/RomDirectory", dir);
        if (_gb.isRunning()) {
            stop();
        }
        start();
    }
}

void MainWindow::start()
{
    QFile file(fileName);
    if (file.exists()) {
        if (!_gb.powerOn(size(), fileName)) {
            QMessageBox msgBox;
            msgBox.setText(tr("Failed to start the emulator."));
            msgBox.exec();
        }
    } else {
        QMessageBox msgBox;
        msgBox.setText(tr("File does not exist."));
        msgBox.exec();
    }
}

void MainWindow::stop()
{
    if (_gb.isRunning()) {
        _gb.stopEmu();
        while (!_gb.wait(100)) {
            printf(".");
        }
        _log->Msg(INFO_LEVEL, LOG_MAIN, "Emulator stopped!");
        update();
    }
}

DebuggerWidget * MainWindow::startDebugger()
{
    DebuggerWidget *ret = NULL;
    if (m_settings->value("EmulatorSettings/Debugger").toBool()) {
        _dbgWidget = new DebuggerWidget(this);
        _dbgWidget->setGameboy(&_gb);
        _dbgWidget->show();
        ret = _dbgWidget;
    }
    return ret;
}

void MainWindow::enableDebugger()
{
    if (m_settings->value("EmulatorSettings/Debugger", false).toBool()) {
        _dbg->enableDebugger(true);
    } else {
        _dbg->enableDebugger(false);
    }
}

void MainWindow::enableFrameLimiter()
{
    if (m_settings->value("EmulatorSettings/FrameLimiter", true).toBool()) {
        _gb.enableFrameLimiter(true);
    } else {
        _gb.enableFrameLimiter(false);
    }
}

void MainWindow::writeSettings()
{
    m_settings->setValue("MainWindow/Size", size());
    m_settings->setValue("MainWindow/Pos", pos());

    m_settings->setValue("Logging/LogLevel", _log->getLogLevel());
    m_settings->setValue("Logging/LogMask", _log->getLogMask());
}

void MainWindow::readSettings()
{
    bool ok;
    int tmp;

    resize(m_settings->value("MainWindow/Size", QSize(160, 144 + 50)).toSize()); // 50 is the height of MenuBar and StatusBar
    move(m_settings->value("MainWindow/Pos", QPoint(200, 200)).toPoint());

    // activate the various settings
    tmp = m_settings->value("Logging/LogLevel", ALL_LEVELS - DEBUG_LEVEL - LOG_LEVEL).toInt(&ok);
    if (ok) {
        _log->setLogLevel(tmp);
    } else {
        _log->setLogLevel(0);
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read the log level!");
    }

    tmp = m_settings->value("Logging/LogMask", ALL_LOGS).toInt(&ok);
    if (ok) {
        _log->setLogMask(tmp);
    } else {
        _log->setLogMask(0);
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read the log mask!");
    }

    enableFrameLimiter();
    enableDebugger();


    // Key Bindings
    m_up = (m_settings->value("KeyBindings/Up", Qt::Key_Up)).toInt(&ok);
    if (!ok) {
        m_up = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read Up value from settings!");
        return;
    }
    m_down = (m_settings->value("KeyBindings/Down", Qt::Key_Down)).toInt(&ok);
    if (!ok) {
        m_down = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read Down value from settings!");
        return;
    }
    m_right = (m_settings->value("KeyBindings/Right", Qt::Key_Right)).toInt(&ok);
    if (!ok) {
        m_right = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read Right value from settings!");
        return;
    }
    m_left = (m_settings->value("KeyBindings/Left", Qt::Key_Left)).toInt(&ok);
    if (!ok) {
        m_left = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read Left value from settings!");
        return;
    }
    m_select = (m_settings->value("KeyBindings/Select", Qt::Key_Shift)).toInt(&ok);
    if (!ok) {
        m_select = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read Select value from settings!");
        return;
    }
    m_start = (m_settings->value("KeyBindings/Start", Qt::Key_Control)).toInt(&ok);
    if (!ok) {
        m_start = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read Start value from settings!");
        return;
    }
    m_a = (m_settings->value("KeyBindings/A", Qt::Key_A)).toInt(&ok);
    if (!ok) {
        m_a = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read A value from settings!");
        return;
    }
    m_b = (m_settings->value("KeyBindings/B", Qt::Key_B)).toInt(&ok);
    if (!ok) {
        m_b = 0;
        _log->Msg(WARNING_LEVEL, LOG_MAIN, "Failed to read B value from settings!");
        return;
    }
}

void MainWindow::showAbout()
{
    AboutDialog d(this);
    d.exec();
}

void MainWindow::showSettings()
{
    SettingsDialog d(this, m_settings, _log);
    d.exec();
    enableFrameLimiter();
    enableDebugger();   // Update the debugger status
}
